﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Caching;
using System.Collections.ObjectModel;
using Framework.Library.Core.Config;
using Framework.Library.Core.Provider.Log;

namespace Framework.Library.Core.Provider.Caches
{
    public class CacheMemory : ICaching
    {
        private ObjectCache Cache { get { return MemoryCache.Default; } }
        private CacheEntryRemovedCallback callback = null;

        public T Get<T>(string key, string parameters = null)
        {
            T value = default(T);
            if (Cache.Contains(CreateKeyWithRegion(key, parameters)))
                value = (T)Cache.Get(CreateKeyWithRegion(key, parameters));
            return value;
        }

        public List<string> GetAll()
        {
            List<string> lst = Cache.Select(keyValuePair => keyValuePair.Key).ToList();
            if (lst.Count > 0)
                return lst;
            return new List<string>();
        }

        public void Set<T>(string key, T data, string parameters = null)
        {
            Set(key, data, MyCachePriority.Default, Configurations.CacheExpire, parameters);
        }

        public void Set<T>(string key, T data, int cacheTime, string parameters = null)
        {
            Set(key, data, MyCachePriority.Default, cacheTime, parameters);
        }

        public void Invalidate()
        {
            List<string> lstEntries = GetAll();
            if (lstEntries.Count > 0)
            {
                foreach (string key in lstEntries)
                {
                    Cache.Remove(key);
                }
            }            
        }

        public void Invalidate(string key, string parameters = null)
        {            
            Cache.Remove(CreateKeyWithRegion(key, parameters));
        }

        public void InvalidateRegion(string key)
        {
            List<string> lstEntries = GetAll();
            if (lstEntries.Count > 0)
            {
                lstEntries = lstEntries.FindAll(x => x.StartsWith(string.Format("key:{0};", key), StringComparison.CurrentCultureIgnoreCase));
                if (lstEntries.Count > 0)
                {
                    foreach (string entry in lstEntries)
                    {
                        Cache.Remove(entry);
                    }
                }
            }
        }

        private void Set<T>(string key, T data, MyCachePriority MyCacheItemPriority, int cacheTime, string parameters = null)
        {
            callback = new CacheEntryRemovedCallback(this.MyCachedItemRemovedCallback);
            CacheItemPolicy policy = new CacheItemPolicy();
            policy.Priority = (MyCacheItemPriority == MyCachePriority.Default) ? CacheItemPriority.Default : CacheItemPriority.NotRemovable;
            policy.RemovedCallback = callback;            
            //var keys = new List<string>();
            //keys.Add(CreateKeyWithRegion(key, string.Empty));
            //policy.ChangeMonitors.Add(Cache.CreateCacheEntryChangeMonitor(keys));
            policy.AbsoluteExpiration = (cacheTime == 0) ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now + TimeSpan.FromHours(cacheTime);
            Cache.Set(new CacheItem(CreateKeyWithRegion(key, parameters), data), policy);
        }

        private string CreateKeyWithRegion(string key, string region)
        {
            return string.Format("key:{0};params:{1}", key, (region == null ? "default_params" : region)).Trim();
        }

        private void MyCachedItemRemovedCallback(CacheEntryRemovedArguments arguments) 
        {             
            // Log these values from arguments list 
            String strLog = String.Concat("Reason: ", arguments.RemovedReason.ToString(), " | Key-Name: ", arguments.CacheItem.Key, " | Value-Object: ", arguments.CacheItem.Value.ToString());
            Logger.Info(strLog);
        }

    }
}
